#include "hash.h"

hash_function g_hash_func;
hash_compare g_compare_hash;

HashTable *hash_table_init(int initial_capacity)
{
    if (initial_capacity <= 0)
    {
        initial_capacity = 16;
    }

    HashTable *table = (HashTable *)malloc(sizeof(HashTable));
    if (table == NULL)
    {
        return NULL;
    }
    
    table->table = (HashNode **)calloc(initial_capacity , sizeof(HashNode*));
    if (table->table == NULL)
    {
        free(table);
        return NULL;
    }
    
    table->capacity = initial_capacity;
    table->size = 0;
    return table;
}

int hash_table_clear(HashTable *table)
{
    if (table == NULL && table->table == NULL)
    {
        return -1;
    }

    for (size_t i = 0; i < table->capacity; i++)
    {
        HashNode* current = table->table[i];
        while (current)
        {
            table->table[i] = current->next;
            free(current->key);
            free(current->value);
            free(current);
            current = table->table[i];
            table->size -= 1;
        }
    }
    free(table->table);
    free(table);

    return 0;
}

int hash_insert(HashTable *table, void *key,int keylen,void* value,int vlen)
{
    if (table == NULL && key == NULL && value == NULL)
    {
        return -1;
    }
    
    unsigned int res_hash = g_hash_func(key,table->capacity);
    HashNode* current = table->table[res_hash];//注意二级指针变为一级指针的过程
    while (current)
    {
        int res = g_compare_hash(key,current->key);
        if (res != 0)
        {
            current = current->next;
        }
        else
        {
            return 1;//已存在
        }
    }

    HashNode *node = (HashNode *)malloc(sizeof(HashNode));
    if (node == NULL)
    {
        return -1;
    }
    node->key = malloc(keylen);
    if (node->key == NULL)
    {
        free(node);
        return -1;
    }
    
    node->value = malloc(vlen);
    if (node->value == NULL)
    {
        free(node);
        free(node->key);
        return -1;
    }

    memcpy(node->key,key,keylen);
    memcpy(node->value,value,vlen);
    node->next = NULL;

    //头插法插入
    node->next = table->table[res_hash];
    table->table[res_hash] = node;

    table->size += 1;

    return 0;
}

HashNode *hash_get(HashTable *table, void *key)
{
    if (table == NULL && key == NULL)
    {
        return NULL;
    }
    
    unsigned int res_hash = g_hash_func(key,table->capacity);
    HashNode* current = table->table[res_hash];

    while (current)
    {
        int res = g_compare_hash(key,current->key);
        if (res != 0)
        {
            current = current->next;
        }
        else
        {
            return current;//已存在
        }
    }
    return NULL;
}

int hash_modify(HashTable *table, void *key, int keylen, void *value, int vlen)
{
    if (table == NULL && key == NULL && value == NULL)
    {
        return -1;
    }
    
    unsigned int res_hash = g_hash_func(key,table->capacity);
    HashNode* current = table->table[res_hash];

    while (current)
    {
        int res = g_compare_hash(key,current->key);
        if (res != 0)
        {
            current = current->next;
        }
        else
        {
            memcpy(current->value,value,vlen);
            return 0;
        }
    }

    return 1;
}

int hash_delete(HashTable *table, void *key)
{
    if (table == NULL && key == NULL)
    {
        return -1;
    }
    
    unsigned int res_hash = g_hash_func(key,table->capacity);
    HashNode* current = table->table[res_hash];

    HashNode* parent = NULL;
    while (current)
    {
        int res = g_compare_hash(key,current->key);
        if (res != 0)
        {
            parent = current;
            current = current->next;
        }
        else
        {
            if (parent == NULL)
            {
                table->table[res_hash] = current->next;
            }
            else
            {
                parent->next = current->next;
            }
            free(current->key);
            free(current->value);
            free(current);
            table->size -= 1;
            return 0;
        }
    }
    return 1;
}
